/*! ========================================================================
** Extended Template and Library Test Suite
** Hermite Curve Test
**
** Copyright (c) 2002 Robert B. Quattlebaum Jr.
**
** This package is free software; you can redistribute it and/or
** modify it under the terms of the GNU General Public License as
** published by the Free Software Foundation; either version 2 of
** the License, or (at your option) any later version.
**
** This package is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
** General Public License for more details.
**
** === N O T E S ===========================================================
**
** ========================================================================= */

#include <ETL/clock>
#include <ETL/hermite>
#include <ETL/angle>
#include <ETL/fastangle>
#include <ETL/fixed>
#include <ETL/surface>
#include <ETL/gaussian>
#include <ETL/calculus>
#include <stdio.h>

using namespace etl;
// using namespace std;

#define HERMITE_TEST_ITERATIONS		(100000)

template <class Angle>
void angle_cos_speed_test(void)
{
    Angle a, b, c, d;
    float tmp, tmp2;

    for (tmp = -1; tmp < 1; tmp += 0.000002f) {
        a = (typename Angle::cos)(tmp);
        b = (typename Angle::cos)(tmp);
        c = (typename Angle::cos)(tmp);
        d = (typename Angle::cos)(tmp);
        tmp2 = ((typename Angle::cos)(a)).get();
        tmp2 = ((typename Angle::cos)(b)).get();
        tmp2 = ((typename Angle::cos)(c)).get();
        tmp2 = ((typename Angle::cos)(d)).get();
    }
}
template <class Angle>
void angle_sin_speed_test(void)
{
    Angle a, b, c, d;
    float tmp, tmp2;

    for (tmp = -1.0; tmp < 1.0; tmp += 0.000002f) {
        a = (typename Angle::sin)(tmp);
        b = (typename Angle::sin)(tmp);
        c = (typename Angle::sin)(tmp);
        d = (typename Angle::sin)(tmp);
        tmp2 = ((typename Angle::sin)(a)).get();
        tmp2 = ((typename Angle::sin)(b)).get();
        tmp2 = ((typename Angle::sin)(c)).get();
        tmp2 = ((typename Angle::sin)(d)).get();
    }
}
template <class Angle>
void angle_tan_speed_test(void)
{
    Angle a, b, c, d;
    float tmp, tmp2;

    for (tmp = -1; tmp < 1; tmp += 0.000002f) {
        a = (typename Angle::tan)(tmp);
        b = (typename Angle::tan)(tmp);
        c = (typename Angle::tan)(tmp);
        d = (typename Angle::tan)(tmp);
        tmp2 = ((typename Angle::tan)(a)).get();
        tmp2 = ((typename Angle::tan)(b)).get();
        tmp2 = ((typename Angle::tan)(c)).get();
        tmp2 = ((typename Angle::tan)(d)).get();
    }
}
template <class Angle, class mytan>
void angle_atan2_speed_test(void)
{
    Angle a, b, c;
    float x, y;

    for (y = -10; y < 10; y += 0.05f)
        for (x = -10; x < 10; x += 0.05f) {
            a = mytan(y, x);
            a = mytan(x, y);
            b = mytan(y, x);
            b = mytan(x, y);
            c = mytan(y, x);
            c = mytan(x, y);
            a = mytan(y, x);
            a = mytan(x, y);
            b = mytan(y, x);
            b = mytan(x, y);
            c = mytan(y, x);
            c = mytan(x, y);
        }
}

int fastangle_speed_test(void)
{
    int ret = 0;
    float
    angle_cos_time,
    fastangle_cos_time,
    angle_tan_time,
    fastangle_tan_time,
    angle_atan2_time,
    fastangle_atan2_time,
    angle_sin_time,
    fastangle_sin_time ;

    etl::clock MyTimer;

    MyTimer.reset();
    angle_cos_speed_test<angle>();
    angle_cos_time = MyTimer();
    printf("angle: Cosine test: %f seconds\n", angle_cos_time);

    MyTimer.reset();
    angle_cos_speed_test<fastangle>();
    fastangle_cos_time = MyTimer();
    printf("fastangle: Cosine test: %f seconds\n", fastangle_cos_time);
    printf("fastangle is %.02f%% faster\n", (angle_cos_time / fastangle_cos_time) * 100.0 - 100.0);

    MyTimer.reset();
    angle_sin_speed_test<angle>();
    angle_sin_time = MyTimer();
    printf("angle: Sine test: %f seconds\n", angle_sin_time);

    MyTimer.reset();
    angle_sin_speed_test<fastangle>();
    fastangle_sin_time = MyTimer();
    printf("fastangle: Sine test: %f seconds\n", fastangle_sin_time);
    printf("fastangle is %.02f%% faster\n", (angle_sin_time / fastangle_sin_time) * 100.0 - 100.0);

    MyTimer.reset();
    angle_tan_speed_test<angle>();
    angle_tan_time = MyTimer();
    printf("angle: Tangent test: %f seconds\n", angle_tan_time);

    MyTimer.reset();
    angle_tan_speed_test<fastangle>();
    fastangle_tan_time = MyTimer();
    printf("fastangle: Tangent test: %f seconds\n", fastangle_tan_time);
    printf("fastangle is %.02f%% faster\n", (angle_tan_time / fastangle_tan_time) * 100.0 - 100.0);

    MyTimer.reset();
    angle_atan2_speed_test<angle, angle::tan>();
    angle_atan2_time = MyTimer();
    printf("angle: arcTangent2 test: %f seconds\n", angle_atan2_time);

    MyTimer.reset();
    angle_atan2_speed_test<fastangle, fastangle::tan>();
    fastangle_atan2_time = MyTimer();
    printf("fastangle: arcTangent2 test: %f seconds\n", fastangle_atan2_time);
    printf("fastangle is %.02f%% faster\n", (angle_atan2_time / fastangle_atan2_time) * 100.0 - 100.0);

    return ret;
}

int surface_and_gaussian_blur_test()
{
    int ret = 0;
    etl::clock MyTimer;
    float endtime;

    {
        surface<float> my_surface(1000, 1000);

        MyTimer.reset();
        gaussian_blur(my_surface.begin(), my_surface.end(), 30, 30);
        endtime = MyTimer();
        printf("surface_and_gaussian_blur_test<float>: %f seconds\n", endtime);
    }

    {
        surface<double> my_surface(1000, 1000);

        MyTimer.reset();
        gaussian_blur(my_surface.begin(), my_surface.end(), 30, 30);
        endtime = MyTimer();
        printf("surface_and_gaussian_blur_test<double>: %f seconds\n", endtime);
    }

    {
        surface<fixed> my_surface(1000, 1000);

        MyTimer.reset();
        gaussian_blur(my_surface.begin(), my_surface.end(), 30, 30);
        endtime = MyTimer();
        printf("surface_and_gaussian_blur_test<fixed>: %f seconds\n", endtime);
    }

    return ret;
}

int hermite_int_test()
{
    int ret = 0;
    hermite<int> Hermie;
    hermite<int>::time_type f;
    int i;

    etl::clock timer;
    etl::clock::value_type t;

    Hermie.p1() = 0;
    Hermie.t1() = 40000;
    Hermie.p2() = 0;
    Hermie.t2() = 40000;

    Hermie.sync();

    {
        float t;

        for (f = 0.0f, i = 0, timer.reset(); i < HERMITE_TEST_ITERATIONS; i++, f += 0.000005f) {
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
        }
    }
    t = timer();

    printf("hermite<int>:time=%f milliseconds\n", t * 1000);
    return ret;
}

int hermite_float_test(void)
{
    int ret = 0;
    float f;
    int i;

    hermite<float> Hermie;
    etl::clock timer;
    double t;

    Hermie.p1() = 0;
    Hermie.t1() = 1;
    Hermie.p2() = 0;
    Hermie.t2() = 1;

    Hermie.sync();

    {
        float t;

        for (f = 0.0f, i = 0, timer.reset(); i < HERMITE_TEST_ITERATIONS; i++, f += 0.000005f) {
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
            t += Hermie(f) + Hermie(f + 0.1f);
        }
    }
    t = timer();

    printf("hermite<float>:time=%f milliseconds\n", t * 1000);
    return ret;
}

int hermite_double_test(void)
{
    int ret = 0, i;
    float f;

    hermite<double> Hermie;
    etl::clock timer;
    double t;

    Hermie.p1() = 0;
    Hermie.t1() = 1;
    Hermie.p2() = 0;
    Hermie.t2() = 1;

    Hermie.sync();

    for (f = 0.0f, i = 0, timer.reset(); i < HERMITE_TEST_ITERATIONS; i++, f += 0.000005f) {
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
        t += Hermie(f) + Hermie(f + 0.1f);
    }

    t = timer();

    printf("hermite<double>:time=%f milliseconds\n", t * 1000);
    return ret;
}

int hermite_fixed_test(void)
{
    int ret = 0;
    int i;
    hermite<fixed> Hermie;
    hermite<fixed>::time_type f;
    hermite<fixed>::time_type inc(0.0005f), inc2(1.10);
    fixed sum(0);

    etl::clock timer;
    double t;

    Hermie.p1() = 0;
    Hermie.t1() = 1;
    Hermie.p2() = 0;
    Hermie.t2() = 1;

    Hermie.sync();

    {
        fixed t;

        for (i = 0, f = 0, timer.reset(); i < HERMITE_TEST_ITERATIONS; i++, f += inc) {
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
            sum += Hermie(f) + Hermie(f + inc2);
        }
    }
    t = timer();

    printf("hermite<fixed>:time=%f milliseconds\n", t * 1000);
    return ret;
}

int hermite_angle_test(void)
{
    int ret = 0, i;
    float f;

    hermite<angle> Hermie;
    etl::clock timer;
    angle tmp;
    double t;

    Hermie.p1() = angle::degrees(0);
    Hermie.t1() = angle::degrees(45);

    Hermie.p2() = angle::degrees(-45);
    Hermie.t2() = angle::degrees(180);

    Hermie.sync();

    for (f = 0.0f, i = 0, timer.reset(); i < HERMITE_TEST_ITERATIONS; i++, f += 0.000005f) {
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
    }

    t = timer();

    fprintf(stderr, "hermite<angle>:time=%f milliseconds\n", t * 1000);

    return ret;
}

int hermite_fastangle_test(void)
{
    int ret = 0, i;
    hermite<fastangle> Hermie;
    hermite<fastangle>::time_type f;

    etl::clock timer;
    fastangle tmp;
    double t;

    Hermie.p1() = fastangle::degrees(0);
    Hermie.t1() = fastangle::degrees(45);

    Hermie.p2() = fastangle::degrees(-45);
    Hermie.t2() = fastangle::degrees(180);

    Hermie.sync();

    for (f = 0.0f, i = 0, timer.reset(); i < HERMITE_TEST_ITERATIONS; i++, f += 0.000005f) {
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
        tmp += Hermie(f) + Hermie(f + 0.1f);
    }

    t = timer();

    fprintf(stderr, "hermite<fastangle>:time=%f milliseconds\n", t * 1000);

    return ret;
}

int main()
{
    int error = 0;

    error += fastangle_speed_test();
    error += surface_and_gaussian_blur_test();
    error += hermite_float_test();
    error += hermite_double_test();
    error += hermite_int_test();
    error += hermite_fixed_test();
    error += hermite_angle_test();
    error += hermite_fastangle_test();

    return error;
}